﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Communergy.Common;

using Communergy.Common.Events;
using Communergy.Data;
using CommunergyUploader.Exporters;

namespace CommunergyUploader.Presenters
{
    public class MeterPresenter
    {
        private IMeterView mView;
        IEnergyMonitorProvider device;
        string localMeterId;
        IRepositoryFactory cacheRepository;
        IDataExportCommandRepository dataExportCommandRepository;


        List<EnergyData> realtimeReadings = new List<EnergyData>();
        List<EnergyData> realtimeEnergyReadingsForUpload = new List<EnergyData>();
        List<EnergyData> hourlyEnergyReadings = new List<EnergyData>();
        List<EnergyData> hourlyEnergyReadingsForUpload = new List<EnergyData>();
        List<EnergyData> dailyEnergyReadings = new List<EnergyData>();
        List<EnergyData> dailyEnergyReadingsForUpload = new List<EnergyData>();
        List<EnergyData> monthlyEnergyReadings = new List<EnergyData>();
        List<EnergyData> monthlyEnergyReadingsForUpload = new List<EnergyData>();

        List<ExportHarness> exportHarnesses = new List<ExportHarness>();

        public MeterPresenter(IMeterView view, IRepositoryFactory cacheRepository, IDataExportCommandRepository dataExportCommandRepository)//, List<IDataExporter> exporters)
        {
            this.mView = view;
            this.cacheRepository = cacheRepository;
            this.dataExportCommandRepository = dataExportCommandRepository;
            this.Initialize();

            SetupDataExportCommands();
        }

        private void SetupDataExportCommands()
        {
            var dataExportCommands = dataExportCommandRepository.GetDataExportCommands();
            foreach (var dataExportCommand in dataExportCommands)
            {
                AddDataExportCommand(dataExportCommand);
            }
        }





        public IMeterView View
        {
            get { return mView; }
        }


        private void Initialize()
        {

            this.mView.InitialiseDevice += new EventHandler<EnergyMonitorProviderEventArgs>(mView_InitialiseDevice);

            this.mView.RegisterMeterOnline += new EventHandler<EventArgs>(mView_RegisterMeterOnline);

            this.mView.GetSingleDayReport += new EventHandler<ReportQueryEventArgs>(mView_GetSingleDay);

            this.mView.StartMonitoring += new EventHandler<EventArgs>(mView_StartMonitoring);

            this.mView.StopMonitoring += new EventHandler<EventArgs>(mView_StopMonitoring);

            this.mView.StartUpload += new EventHandler<EventArgs>(mView_StartUpload);

            this.mView.StopUpload += new EventHandler<EventArgs>(mView_StopUpload);


        }


        private void mView_InitialiseDevice(object sender, EnergyMonitorProviderEventArgs e)
        {
            try
            {
                device = e.EnergyMonitorProvider;
                localMeterId = e.MeterId;
                device.EnergyReading += new NewEnergyReadingHandler(device_EnergyReading);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                throw;
            }
        }

        //TODO - awful method, one big mess. Refactor into smaller methods
        void device_EnergyReading(object sender, EnergyReadingEventArgs re)
        {

            var output = re.EnergyMonitorOutput;
            bool cacheData = true;

            //TODO - create and use the DataAccessLayer, not the repository factory directly

            StringBuilder sb = new StringBuilder("Meter Info: ");
            sb.Append("ReadingTotal: ").Append(output.ReadingDateTime.ToString()).Append("; ");
            //sb.Append("Source: ").Append(output.Source).Append("; ");
            sb.Append("Date meter started: ").Append(output.DateOfBirth.ToShortDateString()).Append("; ");
            if (!string.IsNullOrEmpty(output.MeterId))
            {
                sb.Append("MeterId: ").Append(output.MeterId).Append("; ");
            }



            var newCurrentCostRealtimeReadings = (from sr in output.SensorReadings
                                                  where sr.SensorId == 0
                                                  select sr.LiveReadings).FirstOrDefault();

            if (newCurrentCostRealtimeReadings.Count > 0)
            {
                sb.Append("Live reading: ").Append(string.Join(", ", (from r in newCurrentCostRealtimeReadings select r.AverageWatts.ToString()).ToArray()));
                if (cacheData)
                {
                    CacheNewRealtimeData(newCurrentCostRealtimeReadings);
                }
                UpdateRealtimeData(newCurrentCostRealtimeReadings);
            }


            var newCurrentHistory = (from sr in output.SensorReadings
                                     where sr.SensorId == 0
                                     select sr.History).FirstOrDefault();
            if (newCurrentHistory != null)
            {
                using (IUnitOfWork unitOfWork = cacheRepository.CreateUnitOfWork())
                {
                    var localHistoricCache = cacheRepository.CreateHistoricRepository(unitOfWork);// CommunergyClientFactory.GetLocalHistoricCache();
                    sb.Append("Receiving historical data: ");
                    //hourly
                    if (newCurrentHistory.HourlyReadings.Count > 0)
                    {
                        if (cacheData)
                        {
                            //TODO - hourly readings
                            //localHistoricCache.Insert_ElectricityHourly(localMeterId, newCurrentHistory.HourlyReadings);
                            hourlyEnergyReadingsForUpload = hourlyEnergyReadingsForUpload.Union(newCurrentHistory.HourlyReadings).ToList();
                        }

                        int newHourlyCount = 0;
                        foreach (var newHourly in newCurrentHistory.HourlyReadings)
                        {
                            var checkHourly = hourlyEnergyReadings.Where(hr => hr.ReadingDateTime == newHourly.ReadingDateTime).FirstOrDefault();
                            if (checkHourly == null)
                            {
                                hourlyEnergyReadings.Add(newHourly);
                                newHourlyCount++;
                            }
                        }
                        sb.Append(newHourlyCount).Append(" new hourly records; ");
                        mView.HourlyEnergyReadings = hourlyEnergyReadings;
                    }
                    //daily
                    if (newCurrentHistory.DailyReadings.Count > 0)
                    {
                        if (cacheData)
                        {
                            //TODO - daily readings
                           // localHistoricCache.Insert_ElectricityDaily(localMeterId, newCurrentHistory.DailyReadings);
                            dailyEnergyReadingsForUpload = dailyEnergyReadingsForUpload.Union(newCurrentHistory.DailyReadings).ToList();

                        }
                        int newDailyCount = 0;
                        foreach (var newDaily in newCurrentHistory.DailyReadings)
                        {
                            var checkDaily = dailyEnergyReadings.Where(r => r.ReadingDateTime == newDaily.ReadingDateTime).FirstOrDefault();
                            if (checkDaily == null)
                            {
                                dailyEnergyReadings.Add(newDaily);
                                newDailyCount++;
                            }
                        }
                        sb.Append(newDailyCount).Append(" new daily records; ");
                        mView.DailyEnergyReadings = dailyEnergyReadings;
                    }

                    //monthly
                    if (newCurrentHistory.MonthlyReadings.Count > 0)
                    {
                        if (cacheData)
                        {
                            //TODO - monthly cache
                            //localHistoricCache.Insert_ElectricityMonthly(localMeterId, newCurrentHistory.MonthlyReadings);
                            monthlyEnergyReadingsForUpload = monthlyEnergyReadingsForUpload.Union(newCurrentHistory.MonthlyReadings).ToList();
                        }
                        int newMonthlyCount = 0;
                        foreach (var newMonthly in newCurrentHistory.MonthlyReadings)
                        {
                            var checkMonthly = monthlyEnergyReadings.Where(r => r.ReadingDateTime == newMonthly.ReadingDateTime).FirstOrDefault();
                            if (checkMonthly == null)
                            {
                                monthlyEnergyReadings.Add(newMonthly);
                                newMonthlyCount++;
                            }
                        }
                        sb.Append(newMonthlyCount).Append(" new monthly records; ");
                        mView.MonthlyEnergyReadings = monthlyEnergyReadings;
                    }
                    unitOfWork.Save();
                }

            }

            mView.UpdateStatus(sb.ToString());



        }

        private void UpdateRealtimeData(List<EnergyData> newCurrentCostRealtimeReadings)
        {
            var oldestRecord = DateTime.Now.AddHours(-4);
            realtimeReadings = realtimeReadings.Union(newCurrentCostRealtimeReadings).Where(r => r.ReadingDateTime > oldestRecord).ToList();
            mView.RealtimeEnergyReadings = realtimeReadings;
        }

        private void CacheNewRealtimeData(List<EnergyData> newCurrentCostRealtimeReadings)
        {
            DataAccessLayer da = new DataAccessLayer(cacheRepository);
            da.InsertEnergyData(localMeterId, newCurrentCostRealtimeReadings);
        }



        private void mView_RegisterMeterOnline(object sender, EventArgs e)
        {
            try
            {

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                throw;
            }
        }


        private void mView_GetSingleDay(object sender, ReportQueryEventArgs e)
        {
            try
            {
                var reporter = new Reporter(this.cacheRepository);
                var dailyData = reporter.BasicDailyReport(e.ReportQueryParameters.MeterId, e.ReportQueryParameters.StartDateTime);
                mView.BindSingleDayReport(dailyData);

            }
            catch (Exception ex)
            {
                HandleError(ex);
            }
        }

        private void HandleError(Exception ex)
        {
            mView.UpdateStatus(ex.Message);
        }


        private void mView_StartMonitoring(object sender, EventArgs e)
        {
            try
            {
                if (device != null)
                {
                    device.StartReadings();
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                throw;
            }
        }


        private void mView_StopMonitoring(object sender, EventArgs e)
        {
            try
            {
                if (device != null)
                {
                    device.EndReadings();
                    //device.EnergyReading -= new NewEnergyReadingHandler(device_EnergyReading);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                throw;
            }
        }


        private void mView_StartUpload(object sender, EventArgs e)
        {
            try
            {

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                throw;
            }
        }


        private void mView_StopUpload(object sender, EventArgs e)
        {
            try
            {

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                throw;
            }
        }

        public void AddDataExportCommand(IDataExportCommand dataExportCommand)
        {
            dataExportCommand.DataExportEvent += new DataExportHandler(dataExportCommand_DataExportEvent);
            dataExportCommand.LocalCacheRepository = cacheRepository;
            ExportHarness newExportHarness = new ExportHarness(dataExportCommand);
            newExportHarness.StartExporting();
            exportHarnesses.Add(newExportHarness);

        }

        void dataExportCommand_DataExportEvent(object sender, DataExporterEventArgs args)
        {
            mView.UpdateStatus(args.Message);
        }
    }

}
